/*
 * Copyright (c) 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.libermundi.tapestry.elfinder.commands;

import java.io.File;
import java.sql.Date;
import java.text.Format;
import java.text.SimpleDateFormat;

import org.apache.commons.io.FileUtils;
import org.apache.tapestry5.json.JSONArray;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.Request;
import org.libermundi.tapestry.elfinder.exception.VolumeIOException;
import org.libermundi.tapestry.elfinder.services.ElFinderService;
import org.libermundi.tapestry.elfinder.services.Volume;
import org.libermundi.tapestry.elfinder.services.VolumeOperation;
import org.libermundi.tapestry.elfinder.services.VolumeSource;
import org.libermundi.tapestry.elfinder.util.FilesUtils;

import com.google.common.base.Strings;

/**
 * 
 * @author Martin Papy
 *
 */
public abstract class AbstractCommand implements ElFinderCommand {
	private JSONObject _json = new JSONObject();

	private Request _request;
	private VolumeSource _volumeSource;

	private File _currentWorkingFile;
	private Volume _currentVolume;
	private ElFinderService _elFinderService;
	
	public AbstractCommand(Request request){
		this._request=request;
	}
	
	/* (non-Javadoc)
	 * @see org.libermundi.tapestry.elfinder.commands.ElFinderCommand#getElFinderService()
	 */
	@Override
	public ElFinderService getElFinderService() {
		return _elFinderService;
	}

	/*
	 * (non-Javadoc)
	 * @see org.libermundi.tapestry.elfinder.commands.ElFinderCommand#setVolumeSource(org.libermundi.tapestry.elfinder.services.VolumeSource)
	 */
	@Override
	public void setVolumeSource(VolumeSource volumeSource) {
		_volumeSource = volumeSource;
	}

	/* (non-Javadoc)
	 * @see org.libermundi.tapestry.elfinder.commands.ElFinderCommand#setElFinderService(org.libermundi.tapestry.elfinder.services.ElFinderService)
	 */
	@Override
	public void setElFinderService(ElFinderService service) {
		_elFinderService = service;
	}

	/*
	 * (non-Javadoc)
	 * @see org.libermundi.tapestry.elfinder.commands.ElFinderCommand#initVolumeAndCwd()
	 */
	@Override
	public void initVolumeAndCwd() throws VolumeIOException {
		String targetHash = getParameter("target");
		if(Strings.isNullOrEmpty(targetHash)){
			targetHash = getParameter("current");
		}
		
		if(!Strings.isNullOrEmpty(targetHash)){
			_currentVolume=_volumeSource.getVolumeFromHash(targetHash);
			_currentWorkingFile=_currentVolume.getFileFromHash(targetHash);
		} else {
			_currentVolume=_volumeSource.getDefaultVolume();
			_currentWorkingFile=_currentVolume.getBasePath();
		}
		if(!_currentWorkingFile.exists()){
			_currentWorkingFile.mkdirs();
		}
	}

	protected File getWorkingFile(){
		return _currentWorkingFile;
	}
	
	protected Volume getVolume() {
		return _currentVolume;
	}	
	
	protected Request getRequest() {
		return _request;
	}

	protected String getParameter(String paramName) {
		return _request.getParameter(paramName);
	}

	protected String[] getParameters(String paramName) {
		return _request.getParameters(paramName);
	}

	protected void putResponse(String param, Object value) {
		_json.put(param, value);
	}

	protected Object getResponse(String param) {
		return _json.get(param);
	}
	
	protected JSONObject getJSON(){
		return _json;
	}

	protected boolean mustRunInit() {
		return (_request.getParameter("init") != null);
	}

	/*
	 * Access to underlying Service Methods
	 */

	protected String dateFormat(long time) {
		//TODO : Make is as a parameter
		Format formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
		return formatter.format(new Date(time));
	}

	protected String getMime(File file) {
		if(file.isDirectory()){
			return "directory";
		}
		return FilesUtils.getMimeType(file);
	}

	protected String getFileUrl(File file) throws VolumeIOException {
		return _currentVolume.getClientUrl(file);
	}

	protected String basename(File path) {
		return path.getName();
	}

	protected String getRootUrl() {
		return _currentVolume.getBaseUrl();
	}

	protected File getRootFile() {
		return _currentVolume.getBasePath();
	}

	public boolean checkName(String name) {
		return _currentVolume.isValidFilename(name);
	}

	public boolean isUploadAllow(String fileName) {
		return _currentVolume.isAllowed(fileName);
	}

	public boolean isAllowed(File file) {
		return _currentVolume.isAllowed(file);
	}

	public boolean isSpecialDir(File file) {
		return _currentVolume.isSpecialDir(file);
	}
	
	public void _contentCommand() throws VolumeIOException {
		if(mustRunInit()){
			_initCommand();
		}
		File path = _currentWorkingFile;

		boolean isTree = (getParameter("tree") != null);
		_content(path, isTree);
	}

	protected void _initCommand() throws VolumeIOException{
		_api();
		_options();
	}
	
	protected void _uplMaxSize(){
		double maxUpload = _currentVolume.getMaxUploadSize();
		maxUpload = maxUpload / 1024 / 1024;
		putResponse("uplMaxSize", (int) maxUpload + "M");
	}
	
	protected void _api(){
		putResponse("api ", "2.0");
	}
	
	protected void _options() throws VolumeIOException {
		JSONObject options = new JSONObject();
			options.put("path", _currentVolume.getRelativeUrlPath(_currentWorkingFile));
			options.put("url", _currentVolume.getBaseUrl());
			//options.put("tmbURL", "/tmbURLXXXX/");
			options.put("separator", _currentVolume.getPathSeparator());
			options.put("disabled", new JSONArray());
			options.put("copyOverwrite", 1);
			options.put("archivers", new JSONObject());
		putResponse("options", options);
	}
	
	protected void _tree(File dir) throws VolumeIOException{
		JSONArray tree = new JSONArray();
		tree.put(_infos(dir));
		File[] children = dir.listFiles();
		if (children != null) {
			for (File child : children) {
				if (isAllowed(child) && child.isDirectory()) {
					JSONObject info = _infos(child);
					tree.put(info);
					if(child.isDirectory()){
						_tree(child, tree);
					}
				}
			}
		}
		putResponse("tree", tree);		
	}
	
	/**
	 * @param child
	 * @param tree
	 * @throws VolumeIOException 
	 */
	private void _tree(File dir, JSONArray tree) throws VolumeIOException {
		File[] children = dir.listFiles();
		if (isAllowed(dir)) {
			if (children != null) {
				for (File child : children) {
					if(child.isDirectory()){
						tree.put(_infos(child));
						_tree(child, tree);
					}
				}
			}
		}
	}

	protected void _netDrivers(){
		putResponse("netDrivers", new JSONArray());
	}

	/**
	 * Set current dir info, content and [dirs tree]
	 * @throws VolumeIOException 
	 */
	protected void _content(File path, boolean isTree) throws VolumeIOException {
		_uplMaxSize();
		_cwd(path);
		_files(path, isTree);
		_netDrivers();
	}

	/**
	 * Set current dir info
	 * @throws VolumeIOException 
	 */
	protected void _cwd(File f) throws VolumeIOException {
		JSONObject infos = _infos(f);
		putResponse("cwd", infos);
	}

	/**
	 * Set current dir content
	 * 
	 * @param string
	 *            path current dir path
	 * @return void
	 * @throws VolumeIOException 
	 **/
	protected void _files(File dir, boolean isTree) throws VolumeIOException {
		JSONArray infos = new JSONArray();

		if(isTree){
			// We need to get ALL folders of ALL Volumes.
			for (Volume volume : _volumeSource.getVolumes()) {
				File root = volume.getBasePath();
				infos.put(_infos(root));
				_tree(root, infos);
			}
		} else {
			infos.put(_infos(dir)); // Put the Infos of the Current Working dir first
		}
		File[] children = dir.listFiles();
		if (children != null) {
			for (File child : children) {
				if (isAllowed(child)) {
					JSONObject info = _infos(child);
					if(child.isFile() || (!isTree && child.isDirectory())){
						// We don't add the directories twice if they already
						// Have been added using the _tree
						infos.put(info);
					}
				}
			}
		}
		putResponse("files", infos);
	}
	
	/**
	 * Return file/folder info
	 * 
	 * @param string
	 *            path file path
	 * @return array
	 **/
	/**
	 * @param f
	 * @return
	 * @throws VolumeIOException 
	 */
	protected JSONObject _infos(File f) throws VolumeIOException {
		JSONObject infos = new JSONObject();
			infos.put("mime", getMime(f));
			infos.put("ts", f.lastModified());
			infos.put("read", (_currentVolume.isOperationAllowed(f, VolumeOperation.READ))?1:0);
			infos.put("write", (_currentVolume.isOperationAllowed(f, VolumeOperation.UPDATE))?1:0);
			infos.put("size", FileUtils.sizeOf(f));

			infos.put("hash", _currentVolume.hash(f));
			
			if(f.equals(_currentVolume.getBasePath())){
				infos.put("volumeid", _currentVolume.getId());
			} else {
				infos.put("phash",  _currentVolume.hash(f.getParentFile()));
			}
			
			if(f.isDirectory()){
				for (File child : f.listFiles()) {
					if(child.isDirectory()){
						infos.put("dirs", 1);
						break;
					}
				}
			}
			
			if(_currentVolume.hasThumbnail(f)) {
				infos.put("tmb", _currentVolume.getThumbnailUrl(f));
			}
			
			infos.put("name", getFileBasenameOrRootAlias(f));

		return infos;
	}

	protected String getFileBasenameOrRootAlias(File f) {
		String name;
		if (f.equals(getRootFile())) {
			name = _currentVolume.getAlias();
		} else {
			name = f.getName();
		}
		return name;
	}
}
